package ucd.csi.comp30220.fall2011.oteop.collision;

import java.util.Date;

import ucd.csi.comp30220.fall2011.oteop.flight.FlightStatus;
import ucd.csi.comp30220.fall2011.oteop.navigation.GreatCircle;
import ucd.csi.comp30220.fall2011.oteop.navigation.Position;

/**
 * Checks for flights getting too close to each other
 * 
 * @author David Haddock
 * @author David Harrison
 *
 */
public final class CollisionDetector {
    
	/**
	 * Feet per nautical mile constant 
	 */
    private static final double FEET_PER_NAUTICAL_MILE = 6076.11549;
    
    /**
     * The safe head room in feet defined on initialisation
     */
    private int safeHeadRoom; // feet
    
    /**
     * The safe distance in feet defined by on initialisation 
     */
    private int safeDistance;
    
    /**
     * Constructor for the collision Detector
     * @param safeHeadRoom Safe head room (feet)
     * @param safeDistance Safe distance (feet)
     */
    public CollisionDetector(int safeHeadRoom, int safeDistance) {
        this.safeHeadRoom = safeHeadRoom;
        this.safeDistance = safeDistance;
    }
    
    /**
     * Check for a possible collision between two flights
     * @param flight1 Flight one
     * @param flight2 Flight two
     * @return A collision objec or null
     */
    public Collision checkForCollison(FlightStatus flight1, FlightStatus flight2) {
        
        if (flight1.getAltitude() <= flight2.getAltitude() - safeHeadRoom || 
                flight1.getAltitude() >= flight2.getAltitude() + safeHeadRoom)
            return null;
        
        Position intersection = GreatCircle.intersectionOf(
            flight1.getPosition(), flight1.getCourse(), flight2.getPosition(), flight2.getCourse());
        
        if (intersection == GreatCircle.AMBIGUOUS_INTERSECTION) {
            return new CollisionInfo(GreatCircle.AMBIGUOUS_INTERSECTION, -1, null, new Date(), flight1, flight2);
        }
        
        // Distance each flight must travel to get to the intersection
        double d1, d2;
        
        if (intersection == GreatCircle.INFINITE_INTERSECTIONS) {
            double d = GreatCircle.distanceBetween(flight1.getPosition(), flight2.getPosition());
            double ct =  d / ((double)(flight1.getSpeed() + flight2.getSpeed()));
            d1 = ct * flight1.getSpeed();
            d2 = d - d1;
            intersection = GreatCircle.destinationFrom(flight1.getPosition(), flight1.getCourse(), d1);
        }
        else {
            d1 = GreatCircle.distanceBetween(flight1.getPosition(), intersection);
            d2 = GreatCircle.distanceBetween(flight2.getPosition(), intersection);
        }
        
        // Time (in hours) it takes each flight to get to the intersection
        double t1 = d1/flight1.getSpeed();
        double t2 = d2/flight2.getSpeed();
        
        // How long it takes each flight to fly the safe distance
        double st1 = (double) ((double)(safeDistance/FEET_PER_NAUTICAL_MILE)/(double)flight1.getSpeed());
        double st2 = (double) ((double)(safeDistance/FEET_PER_NAUTICAL_MILE)/(double)flight2.getSpeed());
        
        // Arrival times
        double at1 = t1 - st1;
        double at2 = t2 - st2;
        
        // Departure times
        double dt1 = t1 + st1;
        double dt2 = t2 + st2;
        
        if (at1 > dt2 || at2 > dt1)
            return null;
        
        // Flights are likely to collide. Yikes!
        int collisonAltitude = (flight1.getAltitude() +flight2.getAltitude()) / 2;
        Date expectedAt = new Date();
        expectedAt.setTime(System.currentTimeMillis() + (long) (Math.max(at1,at2)*(60*60*1000)));
        return new CollisionInfo(intersection, collisonAltitude, expectedAt, new Date(), flight1, flight2);
    }
    
    /**
     * Has a collision been detected between two flights
     * @param flight1 Flight one
     * @param flight2 Flight two
     * @return whether a possible collision has been detected
     */
    public boolean isCollisionDetected(FlightStatus flight1, FlightStatus flight2) {
    	return checkForCollison(flight1, flight2) == null? true : false;
    }
}
